﻿using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using System.Web.Routing;

namespace Mvc3Demo.Test.WebCore
{
    public class MvcControllerValidator : DynamicObject
    {
        private readonly IController _controller;
        private IRouteHandler _myRouteHandler = new MvcRouteHandler();

        public MvcControllerValidator(IController controller)
        {
            // _myRouteHandler.GetHttpHandler();
            _controller = controller;
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            var propertyInfo = _controller.GetType().GetProperty(binder.Name);
            if (propertyInfo != null)
            {
                result = propertyInfo.GetValue(_controller, null);
                return true;
            }

            result = null;
            return false;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            var propertyInfo = _controller.GetType().GetProperty(binder.Name);
            if (propertyInfo != null)
            {
                propertyInfo.SetValue(_controller, value, null);
                return true;
            }
            return false;
        }

        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            MethodInfo[] methodInfos = _controller.GetType().GetMethods().Where(x => x.Name == binder.Name).ToArray();
            if (methodInfos.Length > 0)
            {
                Controller tmpController = _controller as Controller;

                /*
                if (tmpController != null)
                {
                    var roodata = new RouteData();
                    roodata.Values.Add("controller", tmpController.GetType().Name.Replace("Controller",""));
                    roodata.Values.Add("action",binder.Name);

                    var reqContx = new RequestContext(tmpController.HttpContext, roodata);
                    _controller.Execute(reqContx);
                }
                */

                var methods = RunSelectionFilters(tmpController.ControllerContext, methodInfos.ToList());

                if (methods.Count > 0)
                {
                    var method = methods[0];
                    var controllerDescriptor = GetControllerDescriptor(tmpController.ControllerContext);
                    ActionDescriptor actionDescriptor = FindAction(tmpController.ControllerContext, controllerDescriptor,
                                                                   binder.Name);

                    if (actionDescriptor != null)
                    {
                        FilterInfo filterInfo = GetFilters(tmpController.ControllerContext, actionDescriptor);
                        var authContex = InvokeAuthorizationFilters(tmpController.ControllerContext,
                                                                    filterInfo.AuthorizationFilters, actionDescriptor);
                    }
                }

                result = methodInfos.Where(x => x.GetParameters().Length == args.Length).FirstOrDefault().Invoke(_controller, args);

                return true;
            }

            result = null;
            return false;
        }


        private static AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
        {
            AuthorizationContext context = new AuthorizationContext(controllerContext, actionDescriptor);
            foreach (IAuthorizationFilter filter in filters)
            {
                filter.OnAuthorization(context);
                // short-circuit evaluation
                if (context.Result != null)
                {
                    break;
                }
            }

            return context;
        }

        private static ControllerDescriptor GetControllerDescriptor(ControllerContext controllerContext)
        {
            Type controllerType = controllerContext.Controller.GetType();
            ControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(controllerType);
            return controllerDescriptor;
        }

        private static ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
        {
            ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);
            return actionDescriptor;
        }


        private static List<MethodInfo> RunSelectionFilters(ControllerContext controllerContext, List<MethodInfo> methodInfos)
        {
            List<MethodInfo> matchesWithSelectionAttributes = new List<MethodInfo>();
            List<MethodInfo> matchesWithoutSelectionAttributes = new List<MethodInfo>();

            foreach (MethodInfo methodInfo in methodInfos)
            {
                ActionMethodSelectorAttribute[] attrs = (ActionMethodSelectorAttribute[])methodInfo.GetCustomAttributes(typeof(ActionMethodSelectorAttribute), true /* inherit */);
                if (attrs.Length == 0)
                {
                    matchesWithoutSelectionAttributes.Add(methodInfo);
                }
                else if (attrs.All(attr => attr.IsValidForRequest(controllerContext, methodInfo)))
                {
                    matchesWithSelectionAttributes.Add(methodInfo);
                }
            }


            return (matchesWithSelectionAttributes.Count > 0) ? matchesWithSelectionAttributes : matchesWithoutSelectionAttributes;
        }

        protected virtual FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            //return actionDescriptor.GetFilters();
            //return  FilterProviders.Providers.GetFilters(controllerContext,actionDescriptor);
            return null;
        }

    }

}
